#ifndef __BUILTINS_HPP_
#define __BUILTINS_HPP_

#include "rep/rep.hpp"
#include "rep/running.hpp"

#include "vm/opcodes.hpp"

#define MAX_BUILTINS 256

using namespace rep;

namespace vm{
  class Builtin{
  public:
    enum StandardBuiltins{
      NOOP = 0,
      INT_ADD = 1,
      INT_NEGATE = 2,
      INT_MULTIPLY = 3,
      INT_DIVIDE = 4,
      PRINT = 5,
      IF = 6
    };
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next)=0;
    virtual ~Builtin(){}
    static Builtin* builtins[MAX_BUILTINS];
    static void initialize();
  private:
    static bool initialized;
  };
  
  class IntAddBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class IntNegateBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class IntMultiplyBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class IntDivideBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class PrintBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class IfBuiltin : public Builtin{
    virtual Continuation execute(Cons* stack, Slot* env, Instruction pc, Continuation standard_next);
  };

  class BuiltinInstructions{
  public:
    static void init(){
      if(initialized){
        return;
      }
      initialized = true;
      Ref ref_add;
      ref_add.kind = UNSIGNED;
      ref_add.un = Builtin::INT_ADD;
      
      intAdd = Instruction(BUILTIN, 2, ref_add);

      Ref ref_print;
      ref_add.kind = UNSIGNED;
      ref_add.un = Builtin::PRINT;
      
      print = Instruction(BUILTIN, 1, ref_print);

    }
    static bool initialized;
    static Instruction intAdd;
    static Instruction print;
  };
  
};

#endif
